/**
 * Project 68th
 */

#include "../include/log4cppuse.h"
#include "../include/EventLoop.h"
#include <sys/eventfd.h>
#include <iostream>
using std::cout;
using std::endl;

/**
 * EventLoop implementation
 */


/**
 * @param acceptor
 */
EventLoop::EventLoop(Acceptor & acceptor) 
: _epfd(createEpollFd())
, _evtList(1024)
, _isLooping(false)
, _acceptor(acceptor)
, _conns()
, _onNewConnection()
, _onMessage()
, _onClose()
, _evtfd(createEventFd())
, _pendings()
, _mutex()
{
    //首先就应该获取listenfd，将其放到红黑树上进行监听
    int listenfd = _acceptor.fd();
    addEpollReadFd(listenfd);

    //代表计数器的文件描述符也应该放到红黑树上进行监听
    addEpollReadFd(_evtfd);
}

EventLoop::~EventLoop() {
    close(_epfd);
    close(_evtfd);
}

/**
 * @return int
 */
int EventLoop::createEpollFd() {
    int fd = epoll_create(10);
    if(fd < 0)
    {
        LOG_ERROR("createEpollFd");
        return -1;
    }

    return fd;
}

/**
 * @param fd
 * @return void
 */
void EventLoop::addEpollReadFd(int fd) {
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;

    int ret = epoll_ctl(_epfd,EPOLL_CTL_ADD,fd,&evt);
    if(ret < 0)
    {
        LOG_ERROR("addEpollReadFd");
        return;
    }
}

/**
 * @param fd
 * @return void
 */
void EventLoop::delEpollReadFd(int fd) {
    struct epoll_event evt;
    evt.data.fd = fd;
    evt.events = EPOLLIN;

    int ret = epoll_ctl(_epfd,EPOLL_CTL_DEL,fd,&evt);
    if(ret < 0)
    {
        LOG_ERROR("delEpollReadFd");
        return;
    }
}

/**
 * @return void
 */
void EventLoop::loop() {
    _isLooping = true;
    while(_isLooping)
    {
        waitEpollFd();
    }
}

/**
 * @return void
 */
void EventLoop::unloop() {
    _isLooping = false;
}


/**
 * @return void
 */
void EventLoop::waitEpollFd() {
    int nready = 0;
    do
    {
        //如何获取vector首个元素的首地址
        //&*_evtList.begin()
        //&_evtList[0]
        //&_evtList.front()
        //_evtList.at(0)
        //_evtList.data()
        //
        //虽然使用了vector来充当就绪列表
        //但是epoll_wait肯定不可能以合法的方式触发vector的动态扩容
        nready = epoll_wait(_epfd,&*_evtList.begin(),_evtList.size(),3000);
    }while(-1 == nready && errno == EINTR);

    if(-1 == nready)
    {
        cout << "nready == -1" << endl;
        return;
    }
    else if(0 == nready)
    {
        // cout << ">> epoll_wait timeout!!!" << endl; 
    }
    else
    {
        //如果某一次epoll_wait执行成功，得到的结果nready等于vector的容量
        //那么意味着现在已经建立的连接比较多，多个客户端都向服务端发送了信息
        //到达就绪列表的容量上限，需要手动地扩容
        //（epoll_wait不可能让vector自动扩容）
        if((int)_evtList.size() == nready)
        {
            _evtList.resize(2 * nready);
        }

        for(int idx = 0; idx < nready; ++idx)
        {
            int listenfd = _acceptor.fd();
            int fd = _evtList[idx].data.fd;

            if(fd == listenfd) //有新的连接请求
            {
                handleNewConnection();
            }
            else if(fd == _evtfd)
            {
                //子线程处理完了数据
                //打包好了function，放到了对应的接收区_pengdings
                //然后对_evtfd对应的计数器进行了写入行为
                //当计数器的值不为0时，epoll可以检测到_evtfd就绪
                handleRead();
                doPendingFunctor();
            }
            else //老的连接上有数据传过来
            {
                handleMessage(fd);
            }
        }
    }
}

/**
 * @return void
 */
void EventLoop::handleNewConnection() {
   int connfd =  _acceptor.accept();

   if(connfd < 0)
   {
       LOG_ERROR("handleNewConnection");
       return;
   }

   //将connfd放到红黑树上进行监听
   addEpollReadFd(connfd);

   //con是一个管理连接对象TcpConncection的智能指针shared_ptr
   TcpConnectionPtr con(new TcpConnection(connfd,this));

   //每一次创建一个TcpConnection
   //就由EventLoop利用自身的function数据成员作为沟通的桥梁
   //将外部测试文件中定义的回调函数关联到TcpConnection的
   //function数据成员
   //
   //因为本身针对三个事件的回调函数就应该与TcpConnection强关联
   con->setNewConnectionCallback(_onNewConnection);
   con->setMessageCallback(_onMessage);
   con->setCloseCallback(_onClose);

   //存放代表连接的文件描述符与连接对象的键值对
   _conns[connfd] = con;
   /* _conns.insert({connfd,con}); */

   //执行回调的细节被封装在TcpConnection中
   con->handleNewConnectionCallback();
}

/**
 * @param fd
 * @return void
 */
void EventLoop::handleMessage(int fd) {
    auto it = _conns.find(fd);
    if(it != _conns.end())
    {
        //先判断这条连接是否已经失效
        bool flag = it->second->isClosed();

        if(flag)
        {
            //执行回调
            /* _onClose(it->second); */
            it->second->handleCloseCallback();

            //如果发现客户端下线了，就从红黑树上
            //删除这个失效的连接对应的文件描述符
            //并从保存连接信息的map中删除相应的记录
            delEpollReadFd(fd);
            _conns.erase(it);
        }
        else
        {
            /* _onMessage(it->second); */
            it->second->handleMessageCallback();
        }
    }
    else
    {
        string log="连接是不存在的";
        // cout << log << endl;
        LOG_ERROR(log.c_str());
        return;
    }
}


void EventLoop::setNewConnectionCallback(TcpConnectionCallback && cb)
{
    //有名字的右值引用，其本身也是左值
    /* &cb; */
    _onNewConnection = std::move(cb);
}
void EventLoop::setMessageCallback(TcpConnectionCallback && cb)
{
    _onMessage = std::move(cb);
}
void EventLoop::setCloseCallback(TcpConnectionCallback && cb)
{
    _onClose = std::move(cb);
}


int EventLoop::createEventFd() {
    int efd = eventfd(0, 0);
    if (efd == -1)
    {
        string log="eventfd error";
        // printf("eventfd\n");
        LOG_ERROR(log.c_str());
        return -1;
    }

    return efd;
}

/**
 * @return void
 */
void EventLoop::handleRead() {
    uint64_t u = 0;
    ssize_t s = read(_evtfd, &u, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
    {
        string log="read error";
        // printf("read error\n");
        LOG_ERROR(log.c_str());
    }
}

/**
 * @return void
 */
void EventLoop::wakeup() {
    uint64_t u = 1;
    ssize_t s = write(_evtfd, &u, sizeof(uint64_t));
    if (s != sizeof(uint64_t))
    {
        
        string log="wakeup error";
        // printf("wakeup error\n");
        LOG_ERROR(log.c_str());
    }
}

void EventLoop::runInLoop(Functor && cb)
{
    {
        lock_guard<mutex> lg(_mutex);
        _pendings.push_back(std::move(cb));
    }
    //对计数器进行写操作
    wakeup();
}

void EventLoop::doPendingFunctor()
{
    vector<Functor> temp;
    {
        lock_guard<mutex> lg(mutex);
        temp.swap(_pendings);
    }

    for(auto & cb : temp)
    {
        cb();
    }
}
